#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <syslog.h>

extern "C" {
#include <pcap.h>
}

#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>

#include <list>
#include <map>
#include <string>

using namespace std;


#include "ip.h"
#include "dns.h"
#include "prio.h"
#include "monlist.h"

EXEC SQL INCLUDE sqlca;

static pthread_mutex_t SqlWriteMutex;

void	SQLConnect(char *connectionInfo)
{
	EXEC SQL BEGIN DECLARE SECTION;
	char	*connection;
	EXEC SQL END DECLARE SECTION;

	connection = connectionInfo;

	EXEC SQL CONNECT TO :connection;
	if(sqlca.sqlcode) {
		cerr << "connect Error # " << sqlca.sqlerrm.sqlerrml << " '" << sqlca.sqlerrm.sqlerrmc << "'" << endl;
		exit(0);
	}
	pthread_mutex_init(&SqlWriteMutex, NULL);
}

static void timevalToDate(const struct timeval *in, char *out)
{
   struct tm *ti = localtime(&in->tv_sec);
	sprintf(out, "%04d-%02d-%02d %02d:%02d:%02d.%d", 
			ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday,
			ti->tm_hour, ti->tm_min, ti->tm_sec,
			in->tv_usec);
}


static unsigned char *escBinary(const unsigned char *in, int inSize)
{
	unsigned char *out = 0;
	unsigned char *oret;
	const unsigned char *inp = in;
	int	outSize = 0;

	for  (int kk = 0; kk < 2; kk++) {
		if (kk == 1) {
			oret = out = new unsigned char[outSize];
			outSize = 0;
			in = inp;
		}
		for (int ii = 0; ii < inSize; ii++, in++) {
			switch (*in) {
			case '\0':
				outSize += 6;	// "\000"
				if (out) {
					memcpy(out, "\\\\000", 6);
					out += 6;
				}
				break;
			case '\'':
				outSize += 3;	// "\'"
				if (out) {
					memcpy(out, "\\\\'", 3);
					out += 3;
				}
				break;
			case '\\':
				outSize += 4;	// "\\"
				if (out) {
					memcpy(out, "\\\\\\\\", 4);
					out += 4;
				}
				break;
			default:
				if (out) 
					*out++ = *in;
				outSize++;
				break;
			}
		}
		if (out)
			*out++ = '\0';
		outSize++;
	}
	return oret;
}
bool	SQlSaveRecord(CRecord *cr, string interfaceName,  struct timeval *now)
{
	struct in_addr kk;
	bool retval = true;
	EXEC SQL BEGIN DECLARE SECTION;
	int	f_src_ip_serial;
	int	f_dst_ip_serial;
	int	f_port_src;
	int 	f_port_dst;
	int	f_protocol_number;
	int 	f_volume;
	int	f_packets;
	char	f_flushed_at[30];
	int	ind_flushed_at;
	char	f_lastUpdate[30];
	char  f_firstUpdate[30];
	const char	*f_interface;	
	char	*f_contents;
	int		ind_contents;
	unsigned char	*f_captured_data;
	int	ind_captured_data;
	EXEC SQL END DECLARE SECTION;

	f_port_src = cr->port_src_m;
	f_src_ip_serial = cr->srcSerial_m;
	f_port_dst = cr->port_dst_m;
	f_dst_ip_serial = cr->dstSerial_m;
	f_protocol_number = cr->ip_proto_m;
	f_volume = cr->volume_m;
	f_packets = cr->packets_m;
	if (now) {
		timevalToDate(now, f_flushed_at);
		ind_flushed_at = 0;
	} else
		ind_flushed_at = -1;
	timevalToDate(&cr->lastUpdate_, f_lastUpdate);
	timevalToDate(&cr->firstUpdate_, f_firstUpdate);
	f_interface = interfaceName.c_str();
	if (cr->data_.size()) {
		f_captured_data = escBinary((const unsigned char *)cr->data_.c_str(), cr->data_.size());
		ind_captured_data = 0;
	} else {
		ind_captured_data = -1;
	}
	pthread_mutex_lock(&SqlWriteMutex);

	EXEC SQL BEGIN;
	EXEC SQL INSERT INTO connections (src_ip_serial, dst_ip_serial,
		port_src, port_dst, 
		protocol_number, 
		first_seen, last_seen, packets,
		volume, interface, flushed_at, captured_data)
			VALUES (:f_src_ip_serial, :f_dst_ip_serial,
				:f_port_src, :f_port_dst,
				:f_protocol_number, 
				:f_firstUpdate, :f_lastUpdate,
				:f_packets,
				:f_volume,
				:f_interface,
				:f_flushed_at:ind_flushed_at,
				:f_captured_data:ind_captured_data);
	
	if (sqlca.sqlcode != 0) {//  && sqlca.sqlerrm.sqlerrml != 69) {
		printf("insert into usage Error %d message '%s' byta '%s'",
					sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc, f_captured_data);
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "insert into usage Error %d message '%s'",
					sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
		EXEC SQL ROLLBACK;
		retval = false;
	} else {
		EXEC SQL COMMIT;
	}
	
	if (ind_captured_data == 0)
		delete []f_captured_data;
	pthread_mutex_unlock(&SqlWriteMutex);
	return retval;
}

int	SQLGetDNS(string resolvedName, const struct in_addr& ip, const char *mac)
{
	int		retCode= 0;
	EXEC SQL BEGIN DECLARE SECTION;
	int 	f_serial;
	char	f_hostname[512];
	char	f_ip_address[16];

	const char	*q_hostname;
	const char	*q_ip_address;
	const char	*q_mac;
	int		ind_mac;
	EXEC SQL END DECLARE SECTION;
	char	ip_char[inet_to_stringSize];


	q_hostname = resolvedName.c_str();
	inet_to_char(ip, ip_char);
	q_ip_address = ip_char;
	if (mac != NULL) {
		ind_mac = 1;
		q_mac = mac;
	} else {
		ind_mac = -1;
	}

	pthread_mutex_lock(&SqlWriteMutex);
	for (;;) {
		EXEC SQL BEGIN;
		EXEC SQL SELECT serial, hostname, ip
				INTO :f_serial, :f_hostname, :f_ip_address
				FROM dnsentry 
				WHERE hostname = :q_hostname AND ip = :q_ip_address;
		
		if (sqlca.sqlcode == 0) {
			retCode = f_serial;
			break;
		} else if (sqlca.sqlcode != ECPG_NOT_FOUND) {
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), "select from dnsentry Error %d message '%s' ip '%s' host '%s'",
					sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc, q_ip_address, q_hostname);
			EXEC SQL ROLLBACK;
			break;
		} else {
			EXEC SQL INSERT INTO dnsentry  (hostname, ip, mac)
					VALUES (:q_hostname, :q_ip_address, :q_mac:ind_mac);

			if (sqlca.sqlcode != 0) {
				syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), "insert into dnsentry Error %d message '%s'",
						sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
				EXEC SQL ROLLBACK;
				break;
			}
			// continue on to do select to get serial and return it.
		}
		EXEC SQL COMMIT;
	}
	pthread_mutex_unlock(&SqlWriteMutex);
	return retCode;
}

